/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#ifndef PLJAVA_INSTRUCTON_PARSER_H
#define PLJAVA_INSTRUCTON_PARSER_H

#include <PLJavaFile.h>
#include <filetypes/PLJavaFrame.h>

namespace PLSDK {

class PLJavaAttributeCode;

/**
 * Parser for instruction code.
 */
class PLJavaInstructionParser {
public:
    /**
     * Constructor.
     *
     * @param newCodeLength The code length.
     * @param newCode The pointer to the code.
     * @param fileData The data of the file.
     * @param newCode The code data.
     */
    PLJavaInstructionParser(u4 newCodeLength, uint8_t *newCode, PLJavaFile *fileData, PLJavaAttributeCode *newCodeData);
    /**
     * Execute the code.
     *
     * @param frame The frame with the stack and local variables for the methode execution.
     * @param invoker The instance of the invoker code.
     */
    void exec(PLJavaFrame *frame, PLJavaFrame *invoker = 0);
private:
    inline u2 readUnsignedShort(u1 first, u1 sec);
    inline u4 readInt(u1 first, u1 sec, u1 third, u1 last);
    inline u1 *writeUnsignedShort(u2 shortValue);
    inline u1 *writeInt(u4 intValue);
    PLJavaFile *createFileIfNotExists(const PLJavaConstTypesUtf8 *className);
    void invokeInstanceMethode(u4 *i, PLJavaFrame *frame);
    void pop(PLJavaFrame *frame);
    void pushByte(u4 *i, PLJavaFrame *frame);
    void aload0(PLJavaFrame *frame, int pos);
    void createNewObject(u4 *i, PLJavaFrame *frame);
    void invokeVirtualInstanceMethod(u4 *i, PLJavaFrame *frame);
    void loadInt0(PLJavaFrame *frame, int pos);
    void storeInt0(PLJavaFrame *frame, int pos);
    void storeRef0(PLJavaFrame *frame, int pos);
    void duplicateTheTop(PLJavaFrame *frame);
    void returnVoid(PLJavaFrame *frame);
    void iadd(PLJavaFrame *frame);
    void anewarray(u4 *i, PLJavaFrame *frame);
    void ireturn(PLJavaFrame *frame, PLJavaFrame *invoker);
    void aastore(PLJavaFrame *frame);
    void aaload(PLJavaFrame *frame);
    void newarray(u4 *i, PLJavaFrame *frame);
    void iastore(PLJavaFrame *frame);
    void bastore(PLJavaFrame *frame);
    void baload(PLJavaFrame *frame);
    void iaload(PLJavaFrame *frame);
    void castore(PLJavaFrame *frame);
    void caload(PLJavaFrame *frame);
    void ldc(u4 *i, PLJavaFrame *frame);
    void storeFloat0(PLJavaFrame *frame, int pos);
    void fload0(PLJavaFrame *frame, int pos);
    void fadd(PLJavaFrame *frame);
    void freturn(PLJavaFrame *frame, PLJavaFrame *invoker);
    void fastore(PLJavaFrame *frame);
    void faload(PLJavaFrame *frame);
    void ldc2_w(u4 *i, PLJavaFrame *frame);
    void dstore0(PLJavaFrame *frame, int pos);
    void astore(u4 *i, PLJavaFrame *frame);
    void aload(u4 *i, PLJavaFrame *frame);
    void dload0(PLJavaFrame *frame, int pos);
    void dadd(PLJavaFrame *frame);
    void dreturn(PLJavaFrame *frame, PLJavaFrame *invoker);
    void dastore(PLJavaFrame *frame);
    void daload(PLJavaFrame *frame);
    void i2b(PLJavaFrame *frame);
    void i2s(PLJavaFrame *frame);
    void sastore(PLJavaFrame *frame);
    void saload(PLJavaFrame *frame);
    void lstore0(PLJavaFrame *frame, int pos);
    void lload0(PLJavaFrame *frame, int pos);
    void ladd(PLJavaFrame *frame);
    void lreturn(PLJavaFrame *frame, PLJavaFrame *invoker);
    void lastore(PLJavaFrame *frame);
    void laload(PLJavaFrame *frame);
    void areturn(PLJavaFrame *frame, PLJavaFrame *invoker);
    void arraylength(PLJavaFrame *frame);
    void athrow(PLJavaFrame *frame);
    void i2l(PLJavaFrame *frame);
    void checkcast(PLJavaFrame *frame);
    void d2f(PLJavaFrame *frame);
    void d2i(PLJavaFrame *frame);
    void d2l(PLJavaFrame *frame);
    void i2d(PLJavaFrame *frame);
    void dcmp(PLJavaFrame *frame, bool dcmpl);
    void ifeq(u4 *i, PLJavaFrame *frame);
    void ifne(u4 *i, PLJavaFrame *frame);
    void iflt(u4 *i, PLJavaFrame *frame);
    void ifge(u4 *i, PLJavaFrame *frame);
    void ifgt(u4 *i, PLJavaFrame *frame);
    void ifle(u4 *i, PLJavaFrame *frame);
    void gotoF(u4 *i);
    void putfield(u4 *i, PLJavaFrame *frame);
    void getfield(u4 *i, PLJavaFrame *frame);
    u4 m_codeLength;
    u1 *m_code;
    PLJavaFile *m_fileData;
    PLJavaAttributeCode *m_codeData;
};

u2 PLJavaInstructionParser::readUnsignedShort(u1 first, u1 sec) {
    return (u2)first << 8 | (u2)sec;
}

u4 PLJavaInstructionParser::readInt(u1 first, u1 sec, u1 third, u1 last) {
    return (u4)first << 24 | (u4)sec << 16 | (u4)third << 8 | (u4)last;
}

u1 *PLJavaInstructionParser::writeUnsignedShort(u2 shortValue) {
    u1 *ret = new u1[2];
    ret[0] = (u1)(((shortValue >> 8) & 0xFF) - 128);
    ret[1] = (u1)( (shortValue & 0xFF) - 128);
    return ret;
}

u1 *PLJavaInstructionParser::writeInt(u4 intValue) {
    u1 *ret = new u1[4];
    ret[0] = (u1)(((intValue >> 24) & 0xFF) - 128);
    ret[1] = (u1)(((intValue >> 16) & 0xFF) - 128);
    ret[2] = (u1)(((intValue >>  8) & 0xFF) - 128);
    ret[3] = (u1)( (intValue & 0xFF) - 128);
    return ret;
}

}

#endif
